// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:_fe_analyzer_shared/src/messages/diagnostic_message.dart'
    show DiagnosticMessageHandler;
import 'package:_fe_analyzer_shared/src/messages/severity.dart'
    show CfeSeverity;
import 'package:front_end/src/codes/cfe_codes.dart' show codeMissingMain;
import 'package:kernel/kernel.dart' show Component;
import 'package:kernel/target/targets.dart' show Target;

import '../api_prototype/compiler_options.dart'
    show CompilerOptions, InvocationMode, Verbosity;
import '../api_prototype/experimental_flags.dart' show ExperimentalFlag;
import '../api_prototype/file_system.dart' show FileSystem, NullFileSystem;
import '../api_prototype/kernel_generator.dart' show CompilerResult;
import '../base/compiler_context.dart' show CompilerContext;
import '../base/processed_options.dart' show ProcessedOptions;
import '../kernel_generator_impl.dart' show generateKernelInternal;
import 'compiler_state.dart' show InitializedCompilerState;
import 'util.dart' show equalLists, equalMaps, equalSets;

export 'package:_fe_analyzer_shared/src/messages/codes.dart'
    show LocatedMessage;
export 'package:_fe_analyzer_shared/src/messages/diagnostic_message.dart'
    show
        CfeDiagnosticMessage,
        DiagnosticMessageHandler,
        getMessageCharOffset,
        getMessageHeaderText,
        getMessageLength,
        getMessageRelatedInformation,
        getMessageUri;
export 'package:_fe_analyzer_shared/src/messages/severity.dart'
    show CfeSeverity;
export 'package:_fe_analyzer_shared/src/parser/async_modifier.dart'
    show AsyncModifier;
export 'package:_fe_analyzer_shared/src/scanner/characters.dart'
    show
        $$,
        $0,
        $9,
        $A,
        $BACKSLASH,
        $CR,
        $DEL,
        $DQ,
        $HASH,
        $LF,
        $LS,
        $PS,
        $TAB,
        $Z,
        $_,
        $a,
        $g,
        $s,
        $z;
export 'package:_fe_analyzer_shared/src/scanner/scanner.dart'
    show isUserDefinableOperator, isMinusOperator;
export 'package:_fe_analyzer_shared/src/util/filenames.dart'
    show nativeToUri, nativeToUriPath, uriPathToNative;
export 'package:_fe_analyzer_shared/src/util/link.dart' show Link, LinkBuilder;
export 'package:_fe_analyzer_shared/src/util/link_implementation.dart'
    show LinkEntry;
export 'package:_fe_analyzer_shared/src/util/relativize.dart'
    show relativizeUri;

export '../api_prototype/compiler_options.dart'
    show
        CompilerOptions,
        InvocationMode,
        Verbosity,
        parseExperimentalFlags,
        parseExperimentalArguments;
export '../api_prototype/const_conditional_simplifier.dart';
export '../api_prototype/constant_evaluator.dart';
export '../api_prototype/experimental_flags.dart'
    show defaultExperimentalFlags, ExperimentalFlag, isExperimentEnabled;
export '../api_prototype/file_system.dart'
    show FileSystem, FileSystemEntity, FileSystemException;
export '../api_prototype/kernel_generator.dart' show kernelForProgram;
export '../api_prototype/language_version.dart'
    show uriUsesLegacyLanguageVersion;
export '../api_prototype/standard_file_system.dart' show DataFileSystemEntity;
export '../api_prototype/try_constant_evaluator.dart';
export '../base/operator.dart' show Operator;
export '../compute_platform_binaries_location.dart'
    show computePlatformBinariesLocation;
export '../kernel/utils.dart' show ByteSink, serializeComponent;
export 'compiler_state.dart' show InitializedCompilerState;

// Coverage-ignore(suite): Not run.
InitializedCompilerState initializeCompiler(
  InitializedCompilerState? oldState,
  Target target,
  Uri? librariesSpecificationUri,
  List<Uri> additionalDills,
  Uri? packagesFileUri, {
  required Map<ExperimentalFlag, bool> explicitExperimentalFlags,
  Map<String, String>? environmentDefines,
  bool verify = false,
  Set<InvocationMode> invocationModes = const <InvocationMode>{},
  Verbosity verbosity = Verbosity.all,
}) {
  additionalDills.sort((a, b) => a.toString().compareTo(b.toString()));

  // We don't check `target` because it doesn't support '==' and each
  // compilation passes a fresh target. However, we pass a logically identical
  // target each time, so it is safe to assume that it never changes.
  if (oldState != null &&
      oldState.options.packagesFileUri == packagesFileUri &&
      oldState.options.librariesSpecificationUri == librariesSpecificationUri &&
      equalLists(oldState.options.additionalDills, additionalDills) &&
      equalMaps(
        oldState.options.explicitExperimentalFlags,
        explicitExperimentalFlags,
      ) &&
      equalMaps(oldState.options.environmentDefines, environmentDefines) &&
      oldState.options.verify == verify &&
      equalSets(oldState.options.invocationModes, invocationModes) &&
      oldState.options.verbosity == verbosity) {
    return oldState;
  }

  CompilerOptions options = new CompilerOptions()
    ..target = target
    ..additionalDills = additionalDills
    ..librariesSpecificationUri = librariesSpecificationUri
    ..packagesFileUri = packagesFileUri
    ..explicitExperimentalFlags = explicitExperimentalFlags
    ..environmentDefines = environmentDefines
    ..errorOnUnevaluatedConstant = environmentDefines != null ? true : false
    ..verify = verify
    ..invocationModes = invocationModes
    ..verbosity = verbosity;

  ProcessedOptions processedOpts = new ProcessedOptions(options: options);

  return new InitializedCompilerState(options, processedOpts);
}

// Coverage-ignore(suite): Not run.
Future<Component?> compile(
  InitializedCompilerState state,
  bool verbose,
  FileSystem fileSystem,
  DiagnosticMessageHandler onDiagnostic,
  List<Uri> inputs,
) async {
  assert(inputs.length == 1);
  CompilerOptions options = state.options;
  options
    ..onDiagnostic = onDiagnostic
    ..verbose = verbose
    ..fileSystem = fileSystem;

  ProcessedOptions processedOpts = state.processedOpts;
  processedOpts.inputs.clear();
  processedOpts.inputs.addAll(inputs);
  processedOpts.clearFileSystemCache();

  CompilerResult? compilerResult = await CompilerContext.runWithOptions(
    processedOpts,
    (CompilerContext context) async {
      CompilerResult compilerResult = await generateKernelInternal(context);
      Component? component = compilerResult.component;
      if (component == null) return null;
      if (component.mainMethod == null) {
        context.options.report(
          context,
          codeMissingMain.withLocation(inputs.single, -1, 0),
          CfeSeverity.error,
        );
        return null;
      }
      return compilerResult;
    },
  );

  // Remove these parameters from [options] - they are no longer needed and
  // retain state from the previous compile. (http://dartbug.com/33708)
  options.onDiagnostic = null;
  options.fileSystem = const NullFileSystem();
  return compilerResult?.component;
}
